// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                     avtWeightedVariableSummationQuery.C                   //
// ************************************************************************* //

#include <avtWeightedVariableSummationQuery.h>

#include <avtBinaryMultiplyExpression.h>
#include <avtEdgeLength.h>
#include <avtRevolvedVolume.h>
#include <avtSourceFromAVTDataset.h>
#include <avtOriginatingSource.h>
#include <avtVMetricArea.h>
#include <avtVMetricVolume.h>
#include <avtConstantCreatorExpression.h>

#include <DebugStream.h>


using     std::string;


// ****************************************************************************
//  Method: avtWeightedVariableSummationQuery constructor
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2004
//
//  Modifications:
//    Kathleen Bonnell, Wed Aug 10 14:05:07 PDT 2005
//    Force only positive volumes, but allow negative values from var.
//
//    Kathleen Bonnell, Fri Feb  3 10:32:12 PST 2006
//    Added revolvedVolume.
//
//    Hank Childs, Wed Apr 28 05:27:24 PDT 2010
//    Add support for edges (1D cross sections)
//
//    Kevin Griffin, Wed Aug 17 19:40:04 PDT 2016
//    Add support for points
//
// ****************************************************************************

avtWeightedVariableSummationQuery::avtWeightedVariableSummationQuery()
    : avtSummationQuery()
{
    length = new avtEdgeLength;
    length->SetOutputVariableName("avt_weights");

    area = new avtVMetricArea;
    area->SetOutputVariableName("avt_weights");

    multiply = new avtBinaryMultiplyExpression;
    multiply->SetOutputVariableName("avt_sum");

    volume = new avtVMetricVolume;
    volume->SetOutputVariableName("avt_weights");
    volume->UseOnlyPositiveVolumes(true);

    revolvedVolume = new avtRevolvedVolume;
    revolvedVolume->SetOutputVariableName("avt_weights");

    constExpr = new avtConstantCreatorExpression;
    constExpr->SetOutputVariableName("avt_weights");

    string vname = "avt_sum";
    SetVariableName(vname);
    SumGhostValues(false);
    SumOnlyPositiveValues(false);
}


// ****************************************************************************
//  Method: avtWeightedVariableSummationQuery destructor
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2004
//
//  Modifications:
//    Kathleen Bonnell, Fri Feb  3 10:32:12 PST 2006
//    Added revolvedVolume.
//
//    Kevin Griffin, Wed Aug 17 19:40:04 PDT 2016
//    Added constExpr
//
// ****************************************************************************

avtWeightedVariableSummationQuery::~avtWeightedVariableSummationQuery()
{
    delete length;
    delete area;
    delete multiply;
    delete volume;
    delete revolvedVolume;
    delete constExpr;
}


// ****************************************************************************
//  Method: avtWeightedVariableSummationQuery::ApplyFilters
//
//  Purpose:
//      Applies the filters to the input.
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2004
//
//  Modifications:
//
//    Kathleen Bonnell, Wed Mar 31 16:20:39 PST 2004
//    Added logic for time-varying case.
//
//    Hank Childs, Thu Apr  8 08:24:12 PDT 2004
//    Allow time varying data to access database, so it can consider more than
//    one timestep.
//
//    Hank Childs, Fri Apr  9 16:25:40 PDT 2004
//    Minimize work done by creating new SIL.
//
//    Kathleen Bonnell, Tue May  4 14:25:07 PDT 2004
//    Set SILRestriction via member restriction, instead of SILUseSet.
//
//    Kathleen Bonnell, Fri Jan  7 15:15:32 PST 2005
//    Rework so that both time-varying and non use artificial pipeline.
//    Only difference is the pipeline spec used.
//
//    Kathleen Bonnell, Fri Feb  3 10:32:12 PST 2006
//    Added revolvedVolume, use it when 2D data and meshcoord type is RZ or ZR.
//
//    Hank Childs, Thu May 11 13:31:55 PDT 2006
//    Add support for filters to inherit from this class and create new
//    variables based on the mesh.
//
//    Kathleen Bonnell, Wed Apr  2 10:20:27 PDT 2008
//    Retrieve the varname from the dataAtts instead of DataRequest, as
//    DataRequest may have the wrong value based on other pipelines sharing
//    the same source.
//
//    Kathleen Bonnell, Tue Jul 29 9:03:15 PDT 2008
//    For better error messages, check if there is an active variable in the
//    data attributes, and if not then retrieve from data request.
//
//    Kathleen Bonnell, Tue Sep 23 08:53:03 PDT 2008
//    Move setting of secondary var "avt_weights" till after the contract
//    has been set for the time-varying case.
//
//    Kevin Griffin, Wed Aug 17 19:40:04 PDT 2016
//    Added support for point data (topo == 0)
//
// ****************************************************************************

avtDataObject_p
avtWeightedVariableSummationQuery::ApplyFilters(avtDataObject_p inData)
{
    //
    // Create an artificial pipeline.
    //
    avtDataset_p ds;
    CopyTo(ds, inData);
    avtSourceFromAVTDataset termsrc(ds);
    avtDataObject_p dob = termsrc.GetOutput();

    //
    // Set up our base class so it is ready to sum.
    //
    string varname;
    if (GetInput()->GetInfo().GetAttributes().ValidActiveVariable())
        varname = GetInput()->GetInfo().GetAttributes().GetVariableName();
    else
        varname = GetInput()->GetOriginatingSource()->GetFullDataRequest()->
                  GetVariable();
    varname = GetVarname(varname);
    SetSumType(varname);

    int topo = GetInput()->GetInfo().GetAttributes().GetTopologicalDimension();

    if(topo == 0)
    {
        debug5 << "WeightedVariableSum using Point" << endl;
        constExpr->SetInput(dob);
        constExpr->SetFilterCreatesSingleton(false);
        constExpr->SetValue(1.0);
        dob = constExpr->GetOutput();
    }
    else if (topo == 1)
    {
        debug5 << "WeightedVariableSum using length" << endl;
        length->SetInput(dob);
        dob = length->GetOutput();
    }
    else if (topo == 2)
    {
        if (GetInput()->GetInfo().GetAttributes().GetMeshCoordType() == AVT_XY)
        {
            debug5 << "WeightedVariableSum using Area" << endl;
            area->SetInput(dob);
            dob = area->GetOutput();
        }
        else
        {
            debug5 << "WeightedVariableSum using RevolvedVolume" << endl;
            revolvedVolume->SetInput(dob);
            dob = revolvedVolume->GetOutput();
        }
    }
    else
    {
        debug5 << "WeightedVariableSum using Volume" << endl;
        volume->SetInput(dob);
        dob = volume->GetOutput();
    }

    //
    // Let the derived type create a new variable if necessary.
    //
    dob = CreateVariable(dob);

    multiply->SetInput(dob);
    multiply->ClearInputVariableNames();
    multiply->AddInputVariableName("avt_weights");
    multiply->AddInputVariableName(varname.c_str());

    //
    // Cause our artificial pipeline to execute.
    //
    avtContract_p contract =
        inData->GetOriginatingSource()->GetGeneralContract();

    if (timeVarying)
    {
        avtDataRequest_p dataRequest = GetInput()->GetOriginatingSource()
                                           ->GetFullDataRequest();
        avtDataRequest_p newDS = new
            avtDataRequest(dataRequest, querySILR);
        newDS->SetTimestep(queryAtts.GetTimeStep());

        contract = new avtContract(newDS, contract->GetPipelineIndex());
    }

    if (CalculateAverage())
    {
        denomVariableName = "avt_weights";
        // State that we want avt_weights as an output, so it doesn't get
        // thrown out after the multiply.
        contract->GetDataRequest()->AddSecondaryVariable("avt_weights");
    }

    multiply->GetOutput()->Update(contract);
    return multiply->GetOutput();
}


// ****************************************************************************
//  Method: avtWeightedVariableSummationQuery::VerifyInput
//
//  Purpose:
//      Now that we have an input, we can determine what the variable units
//      are and tell the base class about it.
//
//  Programmer: Kathleen Bonnell
//  Creation:   July 28, 2004
//
//  Modifications:
//    Kathleen Bonnell, Thu Jan  6 10:34:57 PST 2005
//    Remove TRY-CATCH block in favor of testing for ValidActiveVariable.
//
//    Hank Childs, Thu May 11 14:13:30 PDT 2006
//    Do not set the units if the variable doesn't match the variable of
//    interest.
//
// ****************************************************************************

void
avtWeightedVariableSummationQuery::VerifyInput(void)
{
    //
    // We want to do this in addition to what the base class does, so call the
    // base class' version of this method as well.
    //
    avtSummationQuery::VerifyInput();

    if (GetInput()->GetInfo().GetAttributes().ValidActiveVariable())
    {
        std::string varname1 =
                        GetInput()->GetInfo().GetAttributes().GetVariableName();
        std::string varname2 = GetVarname(varname1);
        if (varname1 == varname2)
        {
            //
            // Set the base class units to be used in output.
            //
            SetUnits(GetInput()->GetInfo().GetAttributes().GetVariableUnits());
        }
    }
}

// ****************************************************************************
//  Method: avtWeightedVariableSummationQuery::GetTimeCurveSpecs
//
//  Purpose:
//    Override default TimeCurveSpecs
//
//  Programmer:  Kathleen Bigags
//  Creation:    Sep 11, 2024
//
//  Modifications:
//
// ****************************************************************************

const MapNode&
avtWeightedVariableSummationQuery::GetTimeCurveSpecs(const QueryAttributes *qa)
{
    timeCurveSpecs["outputCurveLabel"] = "Weighted_sum_" + qa->GetVariables()[0];
    return timeCurveSpecs;
}

